#include "videomaker.h"
#include <QFileDialog>
#include <QSpacerItem>
#include <QMessageBox>
#include <QDir>
#include <QDialogButtonBox>
#include <QImage>
#include <QPainter>
#include <cv.hpp>
#include <highgui.h>

VideoMaker::VideoMaker(QWidget *parent) :
    QDialog(parent)
{
    createInterface();
    this->setLayout(mainVLayout);
    this->setFixedWidth(600);
    allFrames = new QStringList;
}

void VideoMaker::createInterface()
{
    //
    mainVLayout = new QVBoxLayout;

    ltwImageDirList = new QListWidget;
    mainVLayout->addWidget(ltwImageDirList);

    //List widget and add/remove button
    btnAddItem = new QPushButton(tr("Add"));
    btnRemoveItem = new QPushButton(tr("Remove"));
    btnItemUp = new QPushButton(tr("Up"));
    btnItemDown = new QPushButton(tr("Down"));
    listBtnHLayout = new QHBoxLayout;
    listBtnHLayout->addWidget(btnAddItem);
    listBtnHLayout->addWidget(btnRemoveItem);
    listBtnHLayout->addWidget(btnItemUp);
    listBtnHLayout->addWidget(btnItemDown);
    mainVLayout->addLayout(listBtnHLayout);
    connect(btnAddItem, SIGNAL(clicked()), SLOT(onAddItemClicked()));
    connect(btnRemoveItem, SIGNAL(clicked()), SLOT(onRemoveItemClicked()));
    connect(btnItemUp, SIGNAL(clicked()), SLOT(onUpItemClicked()));
    connect(btnItemDown, SIGNAL(clicked()), SLOT(onDownItemClicked()));

    //Input widgets column and row
    QLabel *lblCol = new QLabel(tr("Column:"));
    letCol = new QLineEdit(tr("5"));
    letCol->setFixedWidth(30);
    QLabel *lblRow = new QLabel(tr("Row:"));
    letRow = new QLineEdit(tr("4"));
    letRow->setFixedWidth(30);
    layoutHLayout = new QHBoxLayout;
    layoutHLayout->addWidget(lblCol);
    layoutHLayout->addWidget(letCol);
    layoutHLayout->addWidget(lblRow);
    layoutHLayout->addWidget(letRow);

    //Spacer item
    QSpacerItem *spacer1 = new QSpacerItem(20,0,
                                           QSizePolicy::Minimum,
                                           QSizePolicy::Minimum);
    layoutHLayout->addSpacerItem(spacer1);

    //Input widgets first frame time code
    QLabel *lblFirstFrame = new QLabel(tr("First frame:"));
    letHour = new QLineEdit(tr("00"));
    letHour->setFixedWidth(24);
    QLabel *lblInter1 = new QLabel(tr(":"));
    lblInter1->setFixedWidth(3);
    letMin = new QLineEdit(tr("00"));
    letMin->setFixedWidth(24);
    QLabel *lblInter2 = new QLabel(tr(":"));
    lblInter2->setFixedWidth(3);
    letSec = new QLineEdit(tr("00"));
    letSec->setFixedWidth(24);
    QLabel *lblInter3 = new QLabel(tr(";"));
    lblInter3->setFixedWidth(3);
    letFraNo = new QLineEdit(tr("00"));
    letFraNo->setFixedWidth(24);
    layoutHLayout->addWidget(lblFirstFrame);
    layoutHLayout->addWidget(letHour);
    layoutHLayout->addWidget(lblInter1);
    layoutHLayout->addWidget(letMin);
    layoutHLayout->addWidget(lblInter2);
    layoutHLayout->addWidget(letSec);
    layoutHLayout->addWidget(lblInter3);
    layoutHLayout->addWidget(letFraNo);

    //Spacer item
    QSpacerItem *spacer2 = new QSpacerItem(20,0,
                                           QSizePolicy::Minimum,
                                           QSizePolicy::Minimum);
    layoutHLayout->addSpacerItem(spacer2);

    //Input widgets total number of frames
    QLabel *lblTotal = new QLabel(tr("Total Frames:"));
    letTotal = new QLineEdit(tr("10"));
    letTotal->setFixedWidth(40);
    layoutHLayout->addWidget(lblTotal);
    layoutHLayout->addWidget(letTotal);

    mainVLayout->addLayout(layoutHLayout);

    //Checkbox widgets
    ckbRotate = new QCheckBox(tr("Rotate Image"));
    ckbOrgResolution = new QCheckBox(tr("Original Resolution"));
    ckbHLayout = new QHBoxLayout;
    ckbHLayout->addWidget(ckbRotate);
    ckbHLayout->addWidget(ckbOrgResolution);
    mainVLayout->addLayout(ckbHLayout);

    ckbRepetFramedrop = new QCheckBox(tr("Repeat last frame when frame drops"));
    mainVLayout->addWidget(ckbRepetFramedrop);

    lblSavePath = new QLineEdit;
    btnSelectPath = new QPushButton(tr("..."));
    btnSelectPath->setFixedWidth(25);
    pathHLayout = new QHBoxLayout;
    pathHLayout->addWidget(lblSavePath);
    pathHLayout->addWidget(btnSelectPath);
    mainVLayout->addLayout(pathHLayout);
    connect(btnSelectPath, SIGNAL(clicked()), SLOT(onSeletPathClicked()));

    btnCreateVideo = new QPushButton(tr("Create Video"));
    mainVLayout->addWidget(btnCreateVideo);
    connect(btnCreateVideo, SIGNAL(clicked()), SLOT(onCreateVideoClicked()));

    btnCreatefImages = new QPushButton(tr("Create Video with Combined Images"));
    mainVLayout->addWidget(btnCreatefImages);
    connect(btnCreatefImages, SIGNAL(clicked()), SLOT(onCreatefImagesClicked()));
}

void VideoMaker::onAddItemClicked()
{
    //
    QString newPath = QFileDialog::getExistingDirectory(this,
                           tr("Please select a folder containing video frames"),
                           "/");
    if (!newPath.isEmpty())
        ltwImageDirList->addItem(newPath);
}

void VideoMaker::onRemoveItemClicked()
{
    //
    if (ltwImageDirList->count() > 1)
    {
        int cur = ltwImageDirList->currentRow();
        if (cur <= (ltwImageDirList->count() - 1))
        {
            ltwImageDirList->setCurrentRow(cur+1);
        } else {
            ltwImageDirList->setCurrentRow(cur - 1);
        }
        QListWidgetItem *item = ltwImageDirList->takeItem(cur);
        ltwImageDirList->removeItemWidget(item);
        delete item;
    } else if (ltwImageDirList->count() == 1)
    {
        QListWidgetItem *item = ltwImageDirList->takeItem(0);
        ltwImageDirList->removeItemWidget(item);
        delete item;
    }
}

void VideoMaker::onUpItemClicked()
{
    //
    if (ltwImageDirList->count() > 1)
    {
        int cur = ltwImageDirList->currentRow();
        if (cur > 0) {
            QListWidgetItem *item = ltwImageDirList->takeItem(cur);
            ltwImageDirList->removeItemWidget(item);
            ltwImageDirList->insertItem(cur-1, item);
            ltwImageDirList->setCurrentRow(cur-1);
        }
    }
}

void VideoMaker::onDownItemClicked()
{
    //
    if (ltwImageDirList->count() > 1)
    {
        int cur = ltwImageDirList->currentRow();
        if (cur < ltwImageDirList->count()-1) {
            QListWidgetItem *item = ltwImageDirList->takeItem(cur);
            ltwImageDirList->removeItemWidget(item);
            ltwImageDirList->insertItem(cur+1, item);
            ltwImageDirList->setCurrentRow(cur+1);
        }
    }
}

void VideoMaker::onSeletPathClicked()
{
    QString newPath = QFileDialog::getSaveFileName(this,
                                                   tr("Save video as"),
                                                   lblSavePath->text());
    if (!newPath.isEmpty())
    {
        if (!lblSavePath->text().isEmpty())
            lblSavePath->clear();
        lblSavePath->setText(newPath);
    }
}

void VideoMaker::onCreateVideoClicked()
{
    if (lblSavePath->text().isEmpty())
        onSeletPathClicked();
    if (letTotal->text().isEmpty()) {
        QMessageBox::information(this, tr("Wrong frame total"),
                                 tr("Please input frame total."));
    }
    //Check layout
    int col = letCol->text().toInt();
    int row = letRow->text().toInt();
    if (col*row < ltwImageDirList->count()) {
        QMessageBox::information(this, tr("Wrong layout"),
                                 tr("The product of column and row is smaller than the total of cameras."));
        return;
    }

    //Check save path
    QDir dir;
    if (dir.exists(lblSavePath->text())) {
        int ret = QMessageBox::question(this, tr("File exists"),
                                 tr("File %1 already exists. Replace it?").arg(lblSavePath->text()),
                                 QMessageBox::Yes,
                                 QMessageBox::No);
        if (QMessageBox::No == ret) {
//            qDebug() << "NO";
            return;
        }
    }

    //Loop: 1 check if the frame exists;
    //      2 combine same frames;
    //      3 write into video
    //Finishes when total of frames is enough or
    //         when no more frames exist
    //  Step 1
    QString shour = letHour->text();
    QString smin = letMin->text();
    QString ssec = letSec->text();
    QString snum = letFraNo->text();
    int ret = checkFrame(shour, smin, ssec, snum);
    if (ltwImageDirList->count() == ret)
    {
        QMessageBox::information(this,
                                 tr("Frame doesn't exist"),
                                 tr("Specified frame doesn't exist. Please check if the time code is correct!"));
        return;
    }
    //  Step 2
    int aWidth, aHeight;
    aWidth = col * WIDTH + MARGIN * (col+1);
    aHeight = row * HEIGHT + MARGIN * (row+1);
    int vWidth, vHeight;

    allFrames->clear();
    QStringList lastValidFrame;

    for (int i=0; i<letTotal->text().toInt(); i++)
    {
        QImage resultImg = QImage(aWidth, aHeight, QImage::Format_ARGB32);
        if (resultImg.isNull()) {
            QMessageBox::information(this,
                                     tr("Error"),
                                     tr("Unable to create image!"));
            return;
        }
        QPainter painter(&resultImg);
        for (int j=0; j<ltwImageDirList->count(); j++)
        {
            QString imgPath(checkImage(shour, smin, ssec, snum,
                                       ltwImageDirList->item(j)->text()));
            if (imgPath.isEmpty()) {
                if (ckbRepetFramedrop->isChecked()) {
                    int ret = lastValidFrame.indexOf(tr("%1").arg(j));
                    if (ret > -1)
                        imgPath.append(lastValidFrame.at(j+1));
                }
            } else {
                qDebug()<< "imgPath:" << imgPath;
                int ret = lastValidFrame.indexOf(tr("%1").arg(j));
                if (-1 == ret)
                {
                    lastValidFrame.append(tr("%1").arg(j));
                    lastValidFrame.append(imgPath);
                } else {
                    lastValidFrame.replace(j+1, imgPath);
                }
            }
            if (!imgPath.isEmpty()) {
                QImage newImage(imgPath);
                int x = (j%col) * (WIDTH+MARGIN) + MARGIN;
                int y = (j/col) * (HEIGHT+MARGIN) + MARGIN;
                painter.drawImage(x, y, newImage);
            }
        }
        painter.end();
        QString path(lblSavePath->text());
        int r = path.lastIndexOf("/");
        int l = path.length() - (path.length()-r) + 1;
        QString fPath(path.left(l));
        fPath.append(tr("%1.jpg").arg(i));
        qDebug() << fPath;
        if (!ckbOrgResolution->isChecked()) {
            vWidth = 1920;
            double factor = (double)resultImg.width() / (double)vWidth;
            vHeight = (int) (resultImg.height() / factor);
            QImage imgOutput = resultImg.scaled(vWidth, vHeight);

            imgOutput.save(fPath);
            qDebug() << "Image scaled! New size:" << imgOutput.width() << " " << resultImg.height();
        } else {
            resultImg.save(fPath);
            vWidth = resultImg.width();
            vHeight = resultImg.height();
        }
        ///media/My Passport/testhdraw/temp/C2_01

        allFrames->append(fPath);

        //Move to next frame;
        getNextFrame(shour, smin, ssec, snum);
        int ret = checkFrame(shour, smin, ssec, snum);
        if (ltwImageDirList->count() == ret)
        {
            QMessageBox::information(this,
                                     tr("Frame doesn't exist"),
                                     tr("Specified frame doesn't exist. Stopped!"));
            return;
        }
    }

    int frames = createVideo(allFrames, vWidth, vHeight, lblSavePath->text());
    qDebug() << frames << " frames written!";
}

void VideoMaker::onCreatefImagesClicked()
{
    //
    QString path = QFileDialog::getExistingDirectory(this, tr("Select the folder containing the images"), "/");
    if (!path.isEmpty())
    {
        QDir picdir(path);
        QStringList filters;
        filters << "*.bmp" << "*.jpg" << "*.BMP" << "*.JPG";
        QFileInfoList list = picdir.entryInfoList(filters, QDir::Files, QDir::Name);
        allFrames->clear();
        for (int i=0; i<list.length(); i++)
        {
            allFrames->append(list.at(i).absoluteFilePath());
        }
        if (allFrames->length() > 0)
        {
            QString path = QFileDialog::getSaveFileName(this, tr("Save avi file"),
                                                        "/", "*.avi");
            if (path.isEmpty())
                path = "/home/noname.avi";
            QImage img(allFrames->at(0));
            int ret = createVideo(allFrames, img.width(), img.height(), path);
            qDebug() << ret << " frames written!";
        } else {
            QMessageBox::information(this, tr("Error"), tr("No image in this folder."));
        }
    }
}

int VideoMaker::createVideo(QStringList *frameList, int width, int height, QString savepath)
{
    //Write into video
    qDebug() << "Create video of size (" << width << ", " << height << ")";
    int ret = 0;
    if (!savepath.isEmpty())
    {
        char *videoName = savepath.toLatin1().data();
        CvSize size = cvSize(width, height);
        CvVideoWriter *pWriter = NULL;
        pWriter = cvCreateVideoWriter(videoName,
                                      CV_FOURCC('D', 'I', 'V','X'),
                                      30, size, 1);
        IplImage *pImg = NULL;

        for (int i=0; i<frameList->length(); i++)
        {
            pImg = cvLoadImage(frameList->at(i).toAscii().data(), 1);
            ret += cvWriteFrame(pWriter, pImg);
    //        cvWaitKey(1);
            qDebug() << "Write frame:" << i;
            cvReleaseImage(&pImg);
        }
    }
    return ret;
}

void VideoMaker::getNextFrame(QString &hour, QString &min, QString &sec, QString &num)
{
    //
    int ihour, imin, isec,inum;
    ihour = hour.toInt();
    imin = min.toInt();
    isec = sec.toInt();
    inum = num.toInt();

    inum ++;
    if (30 == inum) {
        inum = 0;
        isec ++;
        if (60 == isec) {
            isec = 0;
            imin ++;
            if (60 == imin) {
                imin = 0;
                ihour ++;
//                if (24 == ihour)    /*unsure*/
//                    ihour = 0;
            }
        }
    }

    hour.clear();
    min.clear();
    sec.clear();
    num.clear();
    if (ihour < 10)
        hour.append(tr("0%1").arg(ihour));
    else
        hour.append(tr("%1").arg(ihour));
    if (imin < 10)
        min.append(tr("0%1").arg(imin));
    else
        min.append(tr("%1").arg(imin));
    if (isec < 10)
        sec.append(tr("0%1").arg(isec));
    else
        sec.append(tr("%1").arg(isec));
    if (inum < 10)
        num.append(tr("0%1").arg(inum));
    else
        num.append(tr("%1").arg(inum));
}

void VideoMaker::getLastFrame(QString &hour, QString &min, QString &sec, QString &num)
{
    //
    int ihour, imin, isec,inum;
    ihour = hour.toInt();
    imin = min.toInt();
    isec = sec.toInt();
    inum = num.toInt();

    inum --;
    if (-1 == inum) {
        inum = 29;
        isec --;
        if (-1 == isec) {
            isec = 59;
            imin --;
            if (-1 == imin) {
                imin = 59;
                ihour --;
//                if (-1 == ihour)    /*unsure*/
//                    ihour = 23;
            }
        }
    }

    hour.clear();
    min.clear();
    sec.clear();
    num.clear();
    if (ihour < 10)
        hour.append(tr("0%1").arg(ihour));
    else
        hour.append(tr("%1").arg(ihour));
    if (imin < 10)
        min.append(tr("0%1").arg(imin));
    else
        min.append(tr("%1").arg(imin));
    if (isec < 10)
        sec.append(tr("0%1").arg(isec));
    else
        sec.append(tr("%1").arg(isec));
    if (inum < 10)
        num.append(tr("0%1").arg(inum));
    else
        num.append(tr("%1").arg(inum));
}

int VideoMaker::checkFrame(QString hour, QString min, QString sec, QString num)
{
    //
    int absent = 0;
    for (int i=0; i<ltwImageDirList->count(); i++)
    {
        QString ret = checkImage(hour, min, sec, num,
                   ltwImageDirList->item(i)->text());
        if (ret.isEmpty()) absent++;
    }
    return absent;
}

//Return true when the according image exists
QString VideoMaker::checkImage(QString hour, QString min, QString sec, QString num, QString path)
{
    //
    QDir dir;
    QString ret;
    QString fullPath(path);
    fullPath.append("/").append(hour).append("-").append(min).append("-").append(sec).append(";").append(num);
    QString bmp, jpg;
    qDebug() << fullPath;
    bmp.append(fullPath).append(".bmp");
    jpg.append(fullPath).append(".jpg");
    if (dir.exists(bmp))
        ret.append(bmp);
    else if (dir.exists(jpg))
        ret.append(jpg);
    return ret;
}
